package client.game;

import static org.lwjgl.opengl.GL11.*;

import general.datastructures.Rectangle;
import general.datastructures.Vector2f;
import general.exceptions.IllegalPlayerCountException;
import general.helperclasses.Math;

import java.io.IOException;
import java.util.Vector;


import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.PixelFormat;

import client.ogl.Texture;
import client.ogl.TextureLoader;


import server.UnitManager;
import server.player.HumanPlayer;
import server.player.Player;
import server.sprite.Sprite;
import server.world.OldMap;


/**
 * 
 * @author Tim Meurer
 * @version 0.5.0
 * @since 0.5.0
 *
 */
public abstract class Game extends Thread{

	protected final String GAME_TITLE;
	protected final boolean FULLSCREEN;
	protected final int HORIZONTAL_RESOLUTION;
	protected final int VERTICAL_RESOLUTION;
	protected final int AA_SAMPLES;
	protected final int FRAMERATE;
	protected final boolean VSYNC;
	
	protected UnitManager unitManager;
	
	protected boolean isFinished = false;
	
	protected Rectangle viewport;
	protected OldMap map;
	
	protected Vector<Player> players;
	protected int playercount;
	
	public OldMap getMap()
	{
		return map;
	}
	
	public Player getPlayer(int id)
	{
		//TODO fehler ueberpruefung
		return players.elementAt(id);
	}
	
	public Rectangle getViewport()
	{
		return this.viewport;
	}
	
	public UnitManager getUnitManager()
	{
		return this.unitManager;
	}
	
	public Game(String title, boolean fullscreen, int horizontal_resolution, int vertical_resolution, int AA_Samples, int target_Framerate, boolean V_Sync)
	{
		this.GAME_TITLE = title;
		this.FULLSCREEN = fullscreen;
		this.VERTICAL_RESOLUTION = vertical_resolution;
		this.HORIZONTAL_RESOLUTION = horizontal_resolution;
		this.AA_SAMPLES = AA_Samples;
		this.FRAMERATE = target_Framerate;
		this.VSYNC = V_Sync;
	}
	
	protected void init() throws LWJGLException, IllegalPlayerCountException, IOException
	{
		viewport = new Rectangle(0, HORIZONTAL_RESOLUTION, 0, VERTICAL_RESOLUTION);
		
		oglInit();
		
		map = OldMap.createTemplateMap(OldMap.TEMPLATE_ISLAND, 2);
		
		unitManager = new UnitManager(this);
		
		players = new Vector<Player>();
		//TODO
		players.add(new HumanPlayer(this, unitManager.getThreadGroup(), "Human-Player",map.getSpawnPoint(0)));
		
	}
	
	private void oglInit() throws LWJGLException
	{
		DisplayMode[] dm = Display.getAvailableDisplayModes();
		
		for(DisplayMode mode :dm)
		{			
			if((mode.getWidth() == HORIZONTAL_RESOLUTION) && (mode.getHeight() == VERTICAL_RESOLUTION) && (mode.getBitsPerPixel() == 32) && (mode.getFrequency()==60) )
			{
				System.out.println(mode.toString());
				Display.setDisplayMode(mode);
				break;
			}
		}
		
		//Display.setDisplayMode(dm[0]);
		
		Display.setTitle(GAME_TITLE);
		Display.setFullscreen(false);
		Display.create(new PixelFormat(8,0,0,AA_SAMPLES));
		
		//vsync
		Display.setVSyncEnabled(VSYNC);
		
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		
		glOrtho(0, Display.getDisplayMode().getWidth(), Display.getDisplayMode().getHeight(), 0, -1, 1);
		
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		
		glEnable(GL_ALPHA_TEST);
		glAlphaFunc(GL_GREATER, 0.4f);
		
		glClearColor(0.1f, 0.5f, 1.0f, 1.0f);
		
		TextureLoader.init();
		TextureLoader.setDirectory("client/assets/textures/");
			
	}
	
	public void run()
	{
		try {
			long oldtime = getTime();
			long gametime = getTime() - oldtime;
			long counter = 0;
			long fpstime = 0;
			this.init();

			while (!isFinished)
			{
				if(counter>100)
				{
					System.out.println("Graphic-FPS: "+(float)counter/((float)(getTime()-fpstime)/1000));
					counter = 0;
					fpstime = getTime();
				}
				
				if (Display.isCloseRequested())
				{
					isFinished = true;
				}
				
				gametime = getTime() - oldtime;
				oldtime = getTime();	
				
				this.update(gametime);
				this.draw(gametime);
				counter++;
				//Display.sync(60);
			}
			
			this.cleanup();
			
		} catch (LWJGLException e) {
			e.printStackTrace();
			System.out.println("Das Spiel wurde unerwartet beendet!");
		} catch (IllegalPlayerCountException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
	}
	
	protected void cleanup()
	{
		unitManager.cleanup();
		
		Display.destroy();
	}
	
	public void drawSprite(int id)
	{
		Sprite s = unitManager.getSprite(id);
		Texture tex = s.getTexture();
		Vector2f wc_position = s.getPosition();
		float rotation = s.getRotation();
		
		if(map.isInMap(Math.worldToMap(wc_position, map.getTilesize())) && viewport.scale_cpy(1.1f).isInside(wc_position))
		{
			glEnableClientState(GL_VERTEX_ARRAY);
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			glEnable(GL_TEXTURE_2D);
			
			glPushMatrix();
			
			tex.bind();		
			
			glTranslatef(Math.worldToScreen(wc_position.x(),viewport.getLeft()), Math.worldToScreen(wc_position.y(),viewport.getTop()), 0f);
			
			glRotatef(rotation, 0, 0, 1.0f);
			//glTranslatef(-texture.getImageWidth()/2, -texture.getImageHeight()/2, 0);
			
			glVertexPointer(2, 0, s.getVertexBuffer());
			glTexCoordPointer(2, 0, s.getTextureBuffer());
			
			glDrawArrays(GL_QUADS, 0, 4);
			
			glPopMatrix();
			
			glDisable(GL_TEXTURE_2D);
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			glDisableClientState(GL_VERTEX_ARRAY);	
		}
	}
	
	public void drawMap()
	{

		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glEnable(GL_TEXTURE_2D);

		glPushMatrix();
		
		for(int mapX=(int)(viewport.getLeft() / map.getTilesize())-1; mapX<=(viewport.getRight() / map.getTilesize())+1; mapX++)
		{
			for(int mapY=(int)(viewport.getTop() / map.getTilesize()-1); mapY<=(viewport.getBottom() / map.getTilesize())+1; mapY++)
			{
				if(map.isInMap(new Vector2f(mapX, mapY)))
				{
					float screenX = Math.mapToScreen(mapX, viewport.getLeft(), map.getTilesize());		
					float screenY = Math.mapToScreen(mapY, viewport.getTop(), map.getTilesize());
					
					map.BindMapTexture(map.getData(mapX, mapY));
					
					glTranslatef(screenX, screenY, 0f);
										
					glVertexPointer(2, 0, map.getVertexBuffer());
					glTexCoordPointer(2, 0, map.getTextureBuffer());
					
					glDrawArrays(GL_QUADS, 0, 4);
					
					glTranslatef(-screenX, -screenY, 0f);
				}
			}
		}
		
		glPopMatrix();
		
		glDisable(GL_TEXTURE_2D);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glDisableClientState(GL_VERTEX_ARRAY);	
	}
	
	public void draw(long gametime)
	{
		glClear(GL_COLOR_BUFFER_BIT);
		glLoadIdentity();
		
		drawMap();
		for(int i=0; i<unitManager.getSpriteCount(); i++)
		{
			drawSprite(i);
		}
		
		Display.update();	
	}
	
	protected void handleInput()
	{
		while(Keyboard.next())
		{
			int key = Keyboard.getEventKey();
			
			if(key == Keyboard.KEY_ESCAPE)
			{
				isFinished = true;
			}
			else if(key == Keyboard.KEY_ADD)
			{
				if(Keyboard.getEventKeyState())
				{
					String name = "Panzer " + (unitManager.getSpriteCount()+1);
					//TODO playerID anpassen
					//Vector2f spawnpos = new Vector2f(Math.screenToMap(Mouse.getX(), viewport.getLeft(), map.getTilesize()), Math.screenToMap(VERTICAL_RESOLUTION-Mouse.getY(), viewport.getLeft(), map.getTilesize()));
					Vector2f sc_spawnpos = new Vector2f(Mouse.getX(),VERTICAL_RESOLUTION-Mouse.getY());
					unitManager.createSprite(name, "tank.png", sc_spawnpos, 1, players.elementAt(0));
				}
			}
		}
		
		if(Keyboard.isKeyDown(Keyboard.KEY_LEFT))
		{
			viewport.move(new Vector2f(-2f, 0));
		}
		else if(Keyboard.isKeyDown(Keyboard.KEY_RIGHT))
		{
			viewport.move(new Vector2f(2f, 0));
		}
		
		if(Keyboard.isKeyDown(Keyboard.KEY_UP))
		{
			viewport.move(new Vector2f(0, -2f));
		}
		else if(Keyboard.isKeyDown(Keyboard.KEY_DOWN))
		{
			viewport.move(new Vector2f(0, 2f));
		}
		
		while(Mouse.next())
		{
			int mouseButton = Mouse.getEventButton();
			
			Vector2f sc_mousePos = new Vector2f(Mouse.getX(),VERTICAL_RESOLUTION-Mouse.getY());
			//System.out.println(mousePos);
						
			if(mouseButton == 0 && Mouse.getEventButtonState())
			{
				((HumanPlayer)players.get(0)).selectSprite(sc_mousePos);
			}
			else if (mouseButton == 1 && Mouse.getEventButtonState()) 
			{
				System.out.println(sc_mousePos);
				((HumanPlayer)players.get(0)).spriteAction(sc_mousePos);	
			}
		}
		
	}
	
	public abstract void update(long gametime);
	
	private static long timerTicksPerSecond = Sys.getTimerResolution();
	
	public static long getTime()
	{
		//return System.currentTimeMillis();
		return (Sys.getTime() * 1000) / timerTicksPerSecond;
	}
	
}
